/*
 * iap.c
 *
 *  Created on: 13-02-2012
 *      Author: Mateusz
 */

#include "LPC13xx.h"
#include <cr_section_macros.h>

#include "iap.h"

const unsigned sector_start_map[MAX_FLASH_SECTOR] = { SECTOR_0_START,
		SECTOR_1_START, SECTOR_2_START, SECTOR_3_START, SECTOR_4_START,
		SECTOR_5_START, SECTOR_6_START, SECTOR_7_START };

const unsigned sector_end_map[MAX_FLASH_SECTOR] = { SECTOR_0_END, SECTOR_1_END,
		SECTOR_2_END, SECTOR_3_END, SECTOR_4_END, SECTOR_5_END, SECTOR_6_END,
		SECTOR_7_END };

uint8_t user_flash_erased;

char flash_buf[FLASH_BUF_SIZE] __attribute__ ((aligned (8)));

unsigned * flash_address;

unsigned param_table[5];
unsigned result_table[4];

void iap_entry(unsigned param_tab[], unsigned result_tab[]) {
	void (*iap)(unsigned[], unsigned[]);

	iap = (void(*)(unsigned[], unsigned[])) IAP_ADDRESS;
	iap(param_tab, result_tab);
}

void erase_sector(unsigned start_sector, unsigned end_sector, unsigned cclk) {
	param_table[0] = ERASE_SECTOR;
	param_table[1] = start_sector;
	param_table[2] = end_sector;
	param_table[3] = cclk;
	iap_entry(param_table, result_table);
}

void prepare_sector(unsigned start_sector, unsigned end_sector, unsigned cclk) {
	param_table[0] = PREPARE_SECTOR_FOR_WRITE;
	param_table[1] = start_sector;
	param_table[2] = end_sector;
	param_table[3] = cclk;
	iap_entry(param_table, result_table);
}

void find_erase_prepare_sector(unsigned cclk, unsigned flash_address) {
	unsigned i;

	__disable_irq();
	for (i = USER_START_SECTOR; i <= MAX_USER_SECTOR; i++) {
		if (flash_address < sector_end_map[i]) {
			if (flash_address == sector_start_map[i]) {
				prepare_sector(i, i, cclk);
				erase_sector(i, i, cclk);
			}
			prepare_sector(i, i, cclk);
			break;
		}
	}
	__enable_irq();
}

void write_data(unsigned cclk, unsigned flash_address,
		unsigned * flash_data_buf, unsigned count) {
	__disable_irq();
	param_table[0] = COPY_RAM_TO_FLASH;
	param_table[1] = flash_address;
	param_table[2] = (unsigned) flash_data_buf;
	param_table[3] = count;
	param_table[4] = cclk;
	iap_entry(param_table, result_table);
	__enable_irq();
}

void erase_user_flash(void) {
	prepare_sector(USER_START_SECTOR, MAX_USER_SECTOR, SystemCoreClock / 1000);
	erase_sector(USER_START_SECTOR, MAX_USER_SECTOR, SystemCoreClock / 1000);
	if (result_table[0] != CMD_SUCCESS) {
		while (1)
			; /* No way to recover. Just let Windows report a write failure */
	}
}

unsigned write_flash(unsigned * dst, char * src, unsigned no_of_bytes) {
	unsigned i;

	/* Store flash start address */
	flash_address = (unsigned *) dst;

	if (no_of_bytes > FLASH_BUF_SIZE)
		no_of_bytes = FLASH_BUF_SIZE;

	memcpy((void *) flash_buf, src, no_of_bytes);

	/* We have accumulated enough bytes to trigger a flash write */
	find_erase_prepare_sector(SystemCoreClock / 1000, (unsigned) flash_address);
	if (result_table[0] != CMD_SUCCESS) {
		while (1)
			; /* No way to recover. Just let Windows report a write failure */
	}
	write_data(SystemCoreClock / 1000, (unsigned) flash_address,
			(unsigned *) flash_buf, FLASH_BUF_SIZE);
	if (result_table[0] != CMD_SUCCESS) {
		while (1)
			; /* No way to recover. Just let Windows report a write failure */
	}

	return (CMD_SUCCESS);
}

